package cn.sylinx.horm.util;

import java.io.Serializable;
import java.lang.invoke.SerializedLambda;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.WeakHashMap;

import cn.sylinx.horm.exception.HORMException;

public abstract class LambdaUtil {

    private LambdaUtil() {
    }

    private static final Map<Class<?>, SerializedLambda> LAMBDA_CACHE = new WeakHashMap<>();

    public static String methodToFieldName(String methodName) {
        if (!methodName.startsWith("get")) {
            throw new HORMException("invalid get method");
        }
        return capitalize(methodName.replaceFirst("get", ""));
    }

    public static String capitalize(String input) {
        return input.substring(0, 1).toLowerCase() + input.substring(1);
    }

    public static String getLambdaFieldName(Serializable lambda) {
        SerializedLambda serializedLambda = computeSerializedLambda(lambda);
        if (serializedLambda == null) {
            throw new HORMException("获取实体列名异常");
        }

        return methodToFieldName(serializedLambda.getImplMethodName());
    }

    public static String getFullMethodName(Serializable lambda) {
        SerializedLambda serializedLambda = computeSerializedLambda(lambda);
        if (serializedLambda == null) {
            throw new HORMException("获取对象异常");
        }
        return serializedLambda.getImplClass().replaceAll("/", ".") + "." + serializedLambda.getImplMethodName();
    }

    public static SerializedLambda computeSerializedLambda(Serializable lambda) {

        Class<?> cl = lambda.getClass();
        SerializedLambda cacheLambda = LAMBDA_CACHE.get(cl);
        if (cacheLambda != null) {
            GLog.debug("SerializedLambda bingo, class:{}", cl);
            return cacheLambda;
        }

        try {
            Method m = cl.getDeclaredMethod("writeReplace");
            m.setAccessible(true);
            Object replacement = m.invoke(lambda);
            if (replacement instanceof SerializedLambda) {
                cacheLambda = (SerializedLambda) replacement;
                LAMBDA_CACHE.put(cl, cacheLambda);
            }
        } catch (Exception e) {
            GLog.error("lambda parse error", e);
            throw new HORMException("lambda parse error", e);
        }

        return cacheLambda;
    }
}
